home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / png.c < prev    next >
Encoding:
C/C++ Source or Header  |  2003-05-08  |  38.5 KB  |  1,287 lines

  1. /*
  2.  * "$Id: png.c,v 1.61.2.8 2003/04/12 15:19:06 bolsh Exp $"
  3.  *
  4.  *   Portable Network Graphics (PNG) plug-in for The GIMP -- an image
  5.  *   manipulation program
  6.  *
  7.  *   Copyright 1997-1998 Michael Sweet (mike@easysw.com) and
  8.  *   Daniel Skarda (0rfelyus@atrey.karlin.mff.cuni.cz).
  9.  *   and 1999-2000 Nick Lamb (njl195@zepler.org.uk)
  10.  *
  11.  *   This program is free software; you can redistribute it and/or modify
  12.  *   it under the terms of the GNU General Public License as published by
  13.  *   the Free Software Foundation; either version 2 of the License, or
  14.  *   (at your option) any later version.
  15.  *
  16.  *   This program is distributed in the hope that it will be useful,
  17.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  *   GNU General Public License for more details.
  20.  *
  21.  *   You should have received a copy of the GNU General Public License
  22.  *   along with this program; if not, write to the Free Software
  23.  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  24.  *
  25.  * Contents:
  26.  *
  27.  *   main()                      - Main entry - just call gimp_main()...
  28.  *   query()                     - Respond to a plug-in query...
  29.  *   run()                       - Run the plug-in...
  30.  *   load_image()                - Load a PNG image into a new image window.
  31.  *   respin_cmap()               - Re-order a Gimp colormap for PNG tRNS
  32.  *   save_image()                - Save the specified image to a PNG file.
  33.  *   save_ok_callback()          - Destroy the save dialog and save the image.
  34.  *   save_compression_callback() - Update the image compression level.
  35.  *   save_interlace_update()     - Update the interlacing option.
  36.  *   save_dialog()               - Pop up the save dialog.
  37.  *
  38.  * Revision History:
  39.  *
  40.  *   see ChangeLog
  41.  */
  42.  
  43. #include "config.h"
  44.  
  45. #include <stdio.h>
  46. #include <stdlib.h>
  47. #include <time.h>
  48.  
  49. #include <gtk/gtk.h>
  50.  
  51. #include <libgimp/gimp.h>
  52. #include <libgimp/gimpui.h>
  53.  
  54. #include <png.h>                /* PNG library definitions */
  55.  
  56. #include "libgimp/stdplugins-intl.h"
  57.  
  58.  
  59. /*
  60.  * Constants...
  61.  */
  62.  
  63. #define PLUG_IN_VERSION  "1.3.4 - 03 September 2002"
  64. #define SCALE_WIDTH      125
  65.  
  66. #define DEFAULT_GAMMA    2.20
  67.  
  68. /*
  69.  * Structures...
  70.  */
  71.  
  72. typedef struct
  73. {
  74.   gint  interlaced;
  75.   gint  compression_level;
  76.   gint  bkgd;
  77.   gint  gama;
  78.   gint  offs;
  79.   gint  phys;
  80.   gint  time;
  81. } PngSaveVals;
  82.  
  83.  
  84. /*
  85.  * Local functions...
  86.  */
  87.  
  88. static void     query                     (void);
  89. static void     run                       (gchar   *name,
  90.                                            gint     nparams,
  91.                                            GimpParam  *param,
  92.                                            gint    *nreturn_vals,
  93.                                            GimpParam **return_vals);
  94.  
  95. static gint32   load_image                (gchar   *filename);
  96. static gint     save_image                (gchar   *filename,
  97.                                            gint32   image_ID,
  98.                                            gint32   drawable_ID,
  99.                                            gint32   orig_image_ID);
  100.  
  101. static void     respin_cmap               (png_structp   pp,
  102.                                            png_infop     info,
  103.                                            guchar       *remap,
  104.                                            gint32        image_ID,
  105.                                            GimpDrawable *drawable);
  106.  
  107. static gint     save_dialog               (void);
  108. static void     save_ok_callback          (GtkWidget     *widget,
  109.                                            gpointer       data);
  110.  
  111. static int find_unused_ia_colour          (guchar *pixels,
  112.                                            int numpixels,
  113.                                            int* colors);
  114.  
  115. /*
  116.  * Globals...
  117.  */
  118.  
  119. GimpPlugInInfo PLUG_IN_INFO =
  120. {
  121.   NULL,  /* init_proc  */
  122.   NULL,  /* quit_proc  */
  123.   query, /* query_proc */
  124.   run,   /* run_proc   */
  125. };
  126.  
  127. PngSaveVals pngvals = 
  128. {
  129.   FALSE,
  130.   6,
  131.   TRUE, FALSE, FALSE, TRUE, TRUE
  132. };
  133.  
  134. static gboolean runme = FALSE;
  135.  
  136. /*
  137.  * 'main()' - Main entry - just call gimp_main()...
  138.  */
  139.  
  140. MAIN()
  141.  
  142. /* Try to find a colour in the palette which isn't actually 
  143.  * used in the image, so that we can use it as the transparency 
  144.  * index. Taken from gif.c */
  145.   
  146. static int find_unused_ia_colour (guchar *pixels,
  147.                                   int numpixels,
  148.                                   int *colors)
  149. {
  150.   int i;
  151.   gboolean ix_used[256];
  152.   gboolean trans_used = FALSE;
  153.  
  154.   for (i = 0; i < *colors; i++)
  155.     {
  156.       ix_used[i] = FALSE;
  157.     }
  158.  
  159.   for (i = 0; i < numpixels; i++)
  160.     {
  161.       /* If there is no alpha, then the index associated with 
  162.        * this pixel is taken */
  163.       if (pixels[i*2 + 1] > 128) 
  164.         ix_used[pixels[i*2]] = TRUE;
  165.       else
  166.         {
  167.           trans_used = TRUE;
  168.         }
  169.     }
  170.   
  171.   // If there is no transparency, ignore alpha.
  172.   if (trans_used == FALSE)
  173.     return -1;
  174.  
  175.   for (i = 0; i < *colors; i++)
  176.     {
  177.       if (ix_used[i] == FALSE)
  178.         {
  179.           return i;
  180.         }
  181.     }
  182.  
  183.   /* Couldn't find an unused colour index within the number of
  184.      bits per pixel we wanted.  Will have to increment the number
  185.      of colours in the image and assign a transparent pixel there. */
  186.   if ((*colors) < 256)
  187.     {
  188.       (*colors)++;
  189.       return ((*colors)-1);
  190.     }
  191.   
  192.   g_message (_("PNG: Couldn't simply reduce colors further.\nSaving as opaque.\n"));
  193.   return (-1);
  194. }
  195.  
  196. /*
  197.  * 'query()' - Respond to a plug-in query...
  198.  */
  199.  
  200. static void
  201. query (void)
  202. {
  203.   static GimpParamDef load_args[] =
  204.   {
  205.     { GIMP_PDB_INT32,      "run_mode",     "Interactive, non-interactive" },
  206.     { GIMP_PDB_STRING,     "filename",     "The name of the file to load" },
  207.     { GIMP_PDB_STRING,     "raw_filename", "The name of the file to load" }
  208.   };
  209.   static GimpParamDef load_return_vals[] =
  210.   {
  211.     { GIMP_PDB_IMAGE,      "image",        "Output image" }
  212.   };
  213.   static gint nload_args = sizeof (load_args) / sizeof (load_args[0]);
  214.   static gint nload_return_vals = (sizeof (load_return_vals) /
  215.                                    sizeof (load_return_vals[0]));
  216.  
  217.   static GimpParamDef   save_args[] =
  218.   {
  219.     { GIMP_PDB_INT32,   "run_mode",     "Interactive, non-interactive" },
  220.     { GIMP_PDB_IMAGE,   "image",        "Input image" },
  221.     { GIMP_PDB_DRAWABLE,        "drawable",     "Drawable to save" },
  222.     { GIMP_PDB_STRING,  "filename",     "The name of the file to save the image in" },
  223.     { GIMP_PDB_STRING,  "raw_filename", "The name of the file to save the image in" },
  224.     { GIMP_PDB_INT32,   "interlace",    "Use Adam7 interlacing?" },
  225.     { GIMP_PDB_INT32,   "compression",  "Deflate Compression factor (0--9)" },
  226.     { GIMP_PDB_INT32,   "bkgd",         "Write bKGD chunk?" },
  227.     { GIMP_PDB_INT32,   "gama",         "Write gAMA chunk?" },
  228.     { GIMP_PDB_INT32,   "offs",         "Write oFFs chunk?" },
  229.     { GIMP_PDB_INT32,   "phys",         "Write tIME chunk?" },
  230.     { GIMP_PDB_INT32,   "time",         "Write pHYs chunk?" }
  231.   };
  232.   static gint nsave_args = sizeof (save_args) / sizeof (save_args[0]);
  233.  
  234.   gimp_install_procedure ("file_png_load",
  235.                           "Loads files in PNG file format",
  236.                           "This plug-in loads Portable Network Graphics (PNG) files.",
  237.                           "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
  238.                           "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
  239.                           PLUG_IN_VERSION,
  240.                           "<Load>/PNG",
  241.                           NULL,
  242.                           GIMP_PLUGIN,
  243.                           nload_args, nload_return_vals,
  244.                           load_args, load_return_vals);
  245.  
  246.   gimp_install_procedure  ("file_png_save",
  247.                            "Saves files in PNG file format",
  248.                            "This plug-in saves Portable Network Graphics (PNG) files.",
  249.                            "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>",
  250.                            "Michael Sweet <mike@easysw.com>, Daniel Skarda <0rfelyus@atrey.karlin.mff.cuni.cz>, Nick Lamb <njl195@zepler.org.uk>",
  251.                            PLUG_IN_VERSION,
  252.                            "<Save>/PNG",
  253.                            "RGB*,GRAY*,INDEXED*",
  254.                            GIMP_PLUGIN,
  255.                            nsave_args, 0,
  256.                            save_args, NULL);
  257.  
  258.   gimp_register_magic_load_handler ("file_png_load",
  259.                                     "png",
  260.                                     "",
  261.                                     "0,string,\211PNG\r\n\032\n");
  262.   gimp_register_save_handler       ("file_png_save",
  263.                                     "png",
  264.                                     "");
  265. }
  266.  
  267.  
  268. /*
  269.  * 'run()' - Run the plug-in...
  270.  */
  271.  
  272. static void
  273. run (gchar   *name,
  274.      gint     nparams,
  275.      GimpParam  *param,
  276.      gint    *nreturn_vals,
  277.      GimpParam **return_vals)
  278. {
  279.   static GimpParam values[2];
  280.   GimpRunModeType  run_mode;
  281.   GimpPDBStatusType   status = GIMP_PDB_SUCCESS;
  282.   gint32        image_ID;
  283.   gint32        drawable_ID;
  284.   gint32        orig_image_ID;
  285.   GimpExportReturnType export = GIMP_EXPORT_CANCEL;
  286.  
  287.   *nreturn_vals = 1;
  288.   *return_vals  = values;
  289.   values[0].type          = GIMP_PDB_STATUS;
  290.   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
  291.  
  292.   if (strcmp (name, "file_png_load") == 0)
  293.     {
  294.       INIT_I18N_UI ();
  295.       image_ID = load_image (param[1].data.d_string);
  296.  
  297.       if (image_ID != -1)
  298.         {
  299.           *nreturn_vals = 2;
  300.           values[1].type         = GIMP_PDB_IMAGE;
  301.           values[1].data.d_image = image_ID;
  302.         }
  303.       else
  304.         {
  305.           status = GIMP_PDB_EXECUTION_ERROR;
  306.         }
  307.     }
  308.   else if (strcmp (name, "file_png_save") == 0)
  309.     {
  310.       INIT_I18N_UI();
  311.  
  312.       run_mode = param[0].data.d_int32;
  313.       image_ID = orig_image_ID = param[1].data.d_int32;
  314.       drawable_ID = param[2].data.d_int32;
  315.     
  316.       /*  eventually export the image */ 
  317.       switch (run_mode)
  318.         {
  319.         case GIMP_RUN_INTERACTIVE:
  320.         case GIMP_RUN_WITH_LAST_VALS:
  321.           gimp_ui_init ("png", FALSE);
  322.           export = gimp_export_image (&image_ID, &drawable_ID, "PNG", 
  323.                                       (GIMP_EXPORT_CAN_HANDLE_RGB |
  324.                                        GIMP_EXPORT_CAN_HANDLE_GRAY |
  325.                                        GIMP_EXPORT_CAN_HANDLE_INDEXED |
  326.                                        GIMP_EXPORT_CAN_HANDLE_ALPHA ));
  327.           if (export == GIMP_EXPORT_CANCEL)
  328.             {
  329.               *nreturn_vals = 1;
  330.               values[0].data.d_status = GIMP_PDB_CANCEL;
  331.               return;
  332.             }
  333.           break;
  334.         default:
  335.           break;
  336.         }
  337.  
  338.       switch (run_mode)
  339.         {
  340.         case GIMP_RUN_INTERACTIVE:
  341.           /*
  342.            * Possibly retrieve data...
  343.            */
  344.           gimp_get_data ("file_png_save", &pngvals);
  345.  
  346.       /*
  347.        * If the image has no transparency, then there is usually
  348.        * no need to save a bKGD chunk.  For more information, see:
  349.        * http://bugzilla.gnome.org/show_bug.cgi?id=92395
  350.        */
  351.       if (! gimp_drawable_has_alpha (drawable_ID))
  352.         pngvals.bkgd = FALSE;
  353.  
  354.           /*
  355.            * Then acquire information with a dialog...
  356.            */
  357.           if (!save_dialog())
  358.             status = GIMP_PDB_CANCEL;
  359.           break;
  360.  
  361.         case GIMP_RUN_NONINTERACTIVE:
  362.           /*
  363.            * Make sure all the arguments are there!
  364.            */
  365.           if (nparams != 12)
  366.             {
  367.               status = GIMP_PDB_CALLING_ERROR;
  368.             }
  369.           else
  370.             {
  371.               pngvals.interlaced        = param[5].data.d_int32;
  372.               pngvals.compression_level = param[6].data.d_int32;
  373.               pngvals.bkgd              = param[7].data.d_int32;
  374.               pngvals.gama              = param[8].data.d_int32;
  375.               pngvals.phys              = param[9].data.d_int32;
  376.               pngvals.offs              = param[10].data.d_int32;
  377.               pngvals.time              = param[11].data.d_int32;
  378.  
  379.               if (pngvals.compression_level < 0 ||
  380.                   pngvals.compression_level > 9)
  381.                 status = GIMP_PDB_CALLING_ERROR;
  382.             };
  383.           break;
  384.  
  385.         case GIMP_RUN_WITH_LAST_VALS:
  386.           /*
  387.            * Possibly retrieve data...
  388.            */
  389.           gimp_get_data ("file_png_save", &pngvals);
  390.           break;
  391.  
  392.         default:
  393.           break;
  394.         };
  395.  
  396.       if (status == GIMP_PDB_SUCCESS)
  397.         {
  398.           if (save_image (param[3].data.d_string,
  399.                           image_ID, drawable_ID, orig_image_ID))
  400.             {
  401.               gimp_set_data ("file_png_save", &pngvals, sizeof (pngvals));
  402.             }
  403.           else
  404.             {
  405.               status = GIMP_PDB_EXECUTION_ERROR;
  406.             }
  407.         }
  408.  
  409.       if (export == GIMP_EXPORT_EXPORT)
  410.         gimp_image_delete (image_ID);
  411.     }
  412.   else
  413.     {
  414.       status = GIMP_PDB_EXECUTION_ERROR;
  415.     }
  416.  
  417.   values[0].data.d_status = status;
  418. }
  419.  
  420.  
  421. /*
  422.  * 'load_image()' - Load a PNG image into a new image window.
  423.  */
  424.  
  425. static gint32
  426. load_image (gchar *filename)    /* I - File to load */
  427. {
  428.   int           i,              /* Looping var */
  429.                 trns,           /* Transparency present */
  430.                 bpp,            /* Bytes per pixel */
  431.                 image_type,     /* Type of image */
  432.                 layer_type,     /* Type of drawable/layer */
  433.                 empty,          /* Number of fully transparent indices */
  434.                 num_passes,     /* Number of interlace passes in file */
  435.                 pass,           /* Current pass in file */
  436.                 tile_height,    /* Height of tile in GIMP */
  437.                 begin,          /* Beginning tile row */
  438.                 end,            /* Ending tile row */
  439.                 num;            /* Number of rows to load */
  440.   FILE          *fp;            /* File pointer */
  441.   volatile gint32 image;        /* Image -- preserved against setjmp() */
  442.   gint32        layer;          /* Layer */
  443.   GimpDrawable  *drawable;      /* Drawable for layer */
  444.   GimpPixelRgn  pixel_rgn;      /* Pixel region for layer */
  445.   png_structp   pp;             /* PNG read pointer */
  446.   png_infop     info;           /* PNG info pointers */
  447.   guchar        **pixels,       /* Pixel rows */
  448.                 *pixel;         /* Pixel data */
  449.   gchar         *progress;      /* Title for progress display... */
  450.   guchar        alpha[256],     /* Index -> Alpha */
  451.                 *alpha_ptr;     /* Temporary pointer */
  452.  
  453.  /*
  454.   * PNG 0.89 and newer have a sane, forwards compatible constructor.
  455.   * Some SGI IRIX users will not have a new enough version though
  456.   */
  457.  
  458. #if PNG_LIBPNG_VER > 88
  459.   pp   = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  460.   info = png_create_info_struct(pp);
  461. #else
  462.   pp = (png_structp)calloc(sizeof(png_struct), 1);
  463.   png_read_init(pp);
  464.  
  465.   info = (png_infop)calloc(sizeof(png_info), 1);
  466. #endif /* PNG_LIBPNG_VER > 88 */
  467.  
  468.   if (setjmp (pp->jmpbuf))
  469.   {
  470.     g_message (_("%s\nPNG error. File corrupted?"), filename);
  471.     return image;
  472.   }
  473.  
  474.   /* initialise image here, thus avoiding compiler warnings */
  475.  
  476.   image= -1;
  477.  
  478.  /*
  479.   * Open the file and initialize the PNG read "engine"...
  480.   */
  481.  
  482.   fp = fopen(filename, "rb");
  483.  
  484.   if (fp == NULL) 
  485.     {
  486.       g_message ("%s\nis not present or is unreadable", filename);
  487.       gimp_quit ();
  488.   }
  489.  
  490.   png_init_io(pp, fp);
  491.  
  492.   if (strrchr(filename, '/') != NULL)
  493.     progress = g_strdup_printf (_("Loading %s:"), strrchr(filename, '/') + 1);
  494.   else
  495.     progress = g_strdup_printf (_("Loading %s:"), filename);
  496.  
  497.   gimp_progress_init(progress);
  498.   g_free (progress);
  499.  
  500.  /*
  501.   * Get the image dimensions and create the image...
  502.   */
  503.  
  504.   png_read_info(pp, info);
  505.  
  506.  /*
  507.   * Latest attempt, this should be my best yet :)
  508.   */
  509.  
  510.   if (info->bit_depth == 16) {
  511.     png_set_strip_16(pp);
  512.   }
  513.  
  514.   if (info->color_type == PNG_COLOR_TYPE_GRAY && info->bit_depth < 8) {
  515.     png_set_expand(pp);
  516.   }
  517.  
  518.   if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8) {
  519.     png_set_packing(pp);
  520.   }
  521.  
  522.  /*
  523.   * Expand G+tRNS to GA, RGB+tRNS to RGBA
  524.   */
  525.  
  526.   if (info->color_type != PNG_COLOR_TYPE_PALETTE &&
  527.                        (info->valid & PNG_INFO_tRNS)) {
  528.     png_set_expand(pp);
  529.   }
  530.  
  531.  /*
  532.   * Turn on interlace handling... libpng returns just 1 (ie single pass)
  533.   * if the image is not interlaced
  534.   */
  535.  
  536.   num_passes = png_set_interlace_handling(pp);
  537.  
  538.  /*
  539.   * Special handling for INDEXED + tRNS (transparency palette)
  540.   */
  541.  
  542. #if PNG_LIBPNG_VER > 99
  543.   if (png_get_valid(pp, info, PNG_INFO_tRNS) &&
  544.       info->color_type == PNG_COLOR_TYPE_PALETTE)
  545.   {
  546.     png_get_tRNS(pp, info, &alpha_ptr, &num, NULL);
  547.     /* Copy the existing alpha values from the tRNS chunk */
  548.     for (i= 0; i < num; ++i)
  549.       alpha[i]= alpha_ptr[i];
  550.     /* And set any others to fully opaque (255)  */
  551.     for (i= num; i < 256; ++i)
  552.       alpha[i]= 255;
  553.     trns= 1;
  554.   } else {
  555.     trns= 0;
  556.   }
  557. #else
  558.     trns= 0;
  559. #endif /* PNG_LIBPNG_VER > 99 */
  560.  
  561.  /*
  562.   * Update the info structures after the transformations take effect
  563.   */
  564.  
  565.   png_read_update_info(pp, info);
  566.   
  567.   switch (info->color_type)
  568.   {
  569.     case PNG_COLOR_TYPE_RGB :           /* RGB */
  570.         bpp        = 3;
  571.         image_type = GIMP_RGB;
  572.         layer_type = GIMP_RGB_IMAGE;
  573.         break;
  574.  
  575.     case PNG_COLOR_TYPE_RGB_ALPHA :     /* RGBA */
  576.         bpp        = 4;
  577.         image_type = GIMP_RGB;
  578.         layer_type = GIMP_RGBA_IMAGE;
  579.         break;
  580.  
  581.     case PNG_COLOR_TYPE_GRAY :          /* Grayscale */
  582.         bpp        = 1;
  583.         image_type = GIMP_GRAY;
  584.         layer_type = GIMP_GRAY_IMAGE;
  585.         break;
  586.  
  587.     case PNG_COLOR_TYPE_GRAY_ALPHA :    /* Grayscale + alpha */
  588.         bpp        = 2;
  589.         image_type = GIMP_GRAY;
  590.         layer_type = GIMP_GRAYA_IMAGE;
  591.         break;
  592.  
  593.     case PNG_COLOR_TYPE_PALETTE :       /* Indexed */
  594.         bpp        = 1;
  595.         image_type = GIMP_INDEXED;
  596.         layer_type = GIMP_INDEXED_IMAGE;
  597.         break;
  598.     default:                            /* Aie! Unknown type */
  599.         g_message (_("%s\nPNG unknown color model"), filename);
  600.         return -1;
  601.   };
  602.  
  603.   image = gimp_image_new(info->width, info->height, image_type);
  604.   if (image == -1)
  605.   {
  606.     g_message("Can't allocate new image\n%s", filename);
  607.     gimp_quit();
  608.   };
  609.  
  610.  /*
  611.   * Create the "background" layer to hold the image...
  612.   */
  613.  
  614.   layer = gimp_layer_new(image, _("Background"), info->width, info->height,
  615.                          layer_type, 100, GIMP_NORMAL_MODE);
  616.   gimp_image_add_layer(image, layer, 0);
  617.  
  618.   /*
  619.    * Find out everything we can about the image resolution
  620.    * This is only practical with the new 1.0 APIs, I'm afraid
  621.    * due to a bug in libpng-1.0.6, see png-implement for details
  622.    */
  623.  
  624. #if PNG_LIBPNG_VER > 99
  625.   if (png_get_valid(pp, info, PNG_INFO_gAMA)) {
  626.     /* I sure would like to handle this, but there's no mechanism to
  627.        do so in Gimp :( */
  628.   }
  629.   if (png_get_valid(pp, info, PNG_INFO_oFFs)) {
  630.     gimp_layer_set_offsets (layer, png_get_x_offset_pixels(pp, info),
  631.                                    png_get_y_offset_pixels(pp, info));
  632.   }
  633.   if (png_get_valid(pp, info, PNG_INFO_pHYs)) {
  634.     gimp_image_set_resolution(image,
  635.          ((double) png_get_x_pixels_per_meter(pp, info)) * 0.0254,
  636.          ((double) png_get_y_pixels_per_meter(pp, info)) * 0.0254);
  637.   }
  638. #endif /* PNG_LIBPNG_VER > 99 */
  639.  
  640.   gimp_image_set_filename(image, filename);
  641.  
  642.  /*
  643.   * Load the colormap as necessary...
  644.   */
  645.  
  646.   empty= 0; /* by default assume no full transparent palette entries */
  647.  
  648.   if (info->color_type & PNG_COLOR_MASK_PALETTE) {
  649.  
  650. #if PNG_LIBPNG_VER > 99
  651.     if (png_get_valid(pp, info, PNG_INFO_tRNS)) {
  652.       for (empty= 0; empty < 256 && alpha[empty] == 0; ++empty);
  653.         /* Calculates number of fully transparent "empty" entries */
  654.  
  655.       gimp_image_set_cmap(image, (guchar *) (info->palette + empty),
  656.                           info->num_palette - empty);
  657.     } else {
  658.       gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
  659.     }
  660. #else
  661.     gimp_image_set_cmap(image, (guchar *)info->palette, info->num_palette);
  662. #endif /* PNG_LIBPNG_VER > 99 */
  663.  
  664.   }
  665.  
  666.  /*
  667.   * Get the drawable and set the pixel region for our load...
  668.   */
  669.  
  670.   drawable = gimp_drawable_get(layer);
  671.  
  672.   gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  673.                       drawable->height, TRUE, FALSE);
  674.  
  675.  /*
  676.   * Temporary buffer...
  677.   */
  678.  
  679.   tile_height = gimp_tile_height ();
  680.   pixel       = g_new(guchar, tile_height * info->width * bpp);
  681.   pixels      = g_new(guchar *, tile_height);
  682.  
  683.   for (i = 0; i < tile_height; i ++)
  684.     pixels[i] = pixel + info->width * info->channels * i;
  685.  
  686.   for (pass = 0; pass < num_passes; pass ++)
  687.   {
  688.    /*
  689.     * This works if you are only reading one row at a time...
  690.     */
  691.  
  692.     for (begin = 0, end = tile_height;
  693.          begin < info->height;
  694.          begin += tile_height, end += tile_height)
  695.     {
  696.       if (end > info->height)
  697.         end = info->height;
  698.  
  699.       num = end - begin;
  700.         
  701.       if (pass != 0) /* to handle interlaced PiNGs */
  702.         gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
  703.                                 drawable->width, num);
  704.  
  705.       png_read_rows(pp, pixels, NULL, num);
  706.  
  707.       gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
  708.                               drawable->width, num);
  709.  
  710.       gimp_progress_update(((double)pass + (double)end / (double)info->height) /
  711.                            (double)num_passes);
  712.     };
  713.   };
  714.  
  715.  /*
  716.   * Done with the file...
  717.   */
  718.  
  719.   png_read_end(pp, info);
  720. #if PNG_LIBPNG_VER < 10200      /* ?? Anyway, this function isn't in 1.2.0*/
  721.   png_read_destroy(pp, info, NULL);
  722. #endif
  723.  
  724.   g_free(pixel);
  725.   g_free(pixels);
  726.   free(pp);
  727.   free(info);
  728.  
  729.   fclose(fp);
  730.  
  731.   if (trns) {
  732.     gimp_layer_add_alpha(layer);
  733.     drawable = gimp_drawable_get(layer);
  734.     gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  735.                         drawable->height, TRUE, FALSE);
  736.  
  737.     pixel  = g_new(guchar, tile_height * drawable->width * 2); /* bpp == 1 */
  738.  
  739.     for (begin = 0, end = tile_height;
  740.          begin < drawable->height;
  741.          begin += tile_height, end += tile_height)
  742.     {
  743.       if (end > drawable->height) end = drawable->height;
  744.       num= end - begin;
  745.  
  746.       gimp_pixel_rgn_get_rect(&pixel_rgn, pixel, 0, begin,
  747.                                 drawable->width, num);
  748.  
  749.       for (i= 0; i < tile_height * drawable->width; ++i) {
  750.         pixel[i*2+1]= alpha [ pixel[i*2] ];
  751.         pixel[i*2]-= empty;
  752.       }
  753.  
  754.       gimp_pixel_rgn_set_rect(&pixel_rgn, pixel, 0, begin,
  755.                               drawable->width, num);
  756.     }
  757.     g_free(pixel);
  758.   }
  759.  
  760.  /*
  761.   * Update the display...
  762.   */
  763.  
  764.   gimp_drawable_flush(drawable);
  765.   gimp_drawable_detach(drawable);
  766.  
  767.   return (image);
  768. }
  769.  
  770.  
  771. /*
  772.  * 'save_image ()' - Save the specified image to a PNG file.
  773.  */
  774.  
  775. static gint
  776. save_image (gchar  *filename,           /* I - File to save to */
  777.             gint32  image_ID,           /* I - Image to save */
  778.             gint32  drawable_ID,        /* I - Current drawable */
  779.             gint32  orig_image_ID)      /* I - Original image before export */
  780. {
  781.   int           i, k,           /* Looping vars */
  782.                 bpp = 0,        /* Bytes per pixel */
  783.                 type,           /* Type of drawable/layer */
  784.                 num_passes,     /* Number of interlace passes in file */
  785.                 pass,           /* Current pass in file */
  786.                 tile_height,    /* Height of tile in GIMP */
  787.                 begin,          /* Beginning tile row */
  788.                 end,            /* Ending tile row */
  789.                 num;            /* Number of rows to load */
  790.   FILE          *fp;            /* File pointer */
  791.   GimpDrawable  *drawable;      /* Drawable for layer */
  792.   GimpPixelRgn  pixel_rgn;      /* Pixel region for layer */
  793.   png_structp   pp;             /* PNG read pointer */
  794.   png_infop     info;           /* PNG info pointer */
  795.   gint          num_colors;     /* Number of colors in colormap */
  796.   gint          offx, offy;     /* Drawable offsets from origin */
  797.   guchar        **pixels,       /* Pixel rows */
  798.                 *fixed,         /* Fixed-up pixel data */
  799.                 *pixel;         /* Pixel data */
  800.   gchar         *progress;      /* Title for progress display... */
  801.   gdouble       xres, yres;     /* GIMP resolution (dpi) */
  802.   gdouble       gamma;          /* GIMP gamma e.g. 2.20 */
  803.   png_color_16  background;     /* Background color */
  804.   png_time      mod_time;       /* Modification time (ie NOW) */
  805.   guchar        red, green,
  806.                 blue;           /* Used for palette background */
  807.   time_t        cutime;         /* Time since epoch */
  808.   struct tm     *gmt;           /* GMT broken down */
  809.  
  810.   guchar remap [256];    /* Re-mapping for the palette */
  811.  
  812.  /*
  813.   * PNG 0.89 and newer have a sane, forwards compatible constructor.
  814.   * Some SGI IRIX users will not have a new enough version though
  815.   */
  816.  
  817. #if PNG_LIBPNG_VER > 88
  818.   pp   = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  819.   info = png_create_info_struct(pp);
  820. #else
  821.   pp = (png_structp)calloc(sizeof(png_struct), 1);
  822.   png_write_init(pp);
  823.  
  824.   info = (png_infop)calloc(sizeof(png_info), 1);
  825. #endif /* PNG_LIBPNG_VER > 88 */
  826.  
  827.   if (setjmp (pp->jmpbuf))
  828.   {
  829.     g_message (_("%s\nPNG error. Couldn't save image"), filename);
  830.     return 0;
  831.   }
  832.  
  833.  /*
  834.   * Open the file and initialize the PNG write "engine"...
  835.   */
  836.  
  837.   fp = fopen(filename, "wb");
  838.   if (fp == NULL) {
  839.     g_message (_("%s\nCouldn't create file"), filename);
  840.     return 0;
  841.   }
  842.  
  843.   png_init_io(pp, fp);
  844.  
  845.   if (strrchr(filename, '/') != NULL)
  846.     progress = g_strdup_printf (_("Saving %s:"), strrchr(filename, '/') + 1);
  847.   else
  848.     progress = g_strdup_printf (_("Saving %s:"), filename);
  849.  
  850.   gimp_progress_init(progress);
  851.   g_free (progress);
  852.  
  853.  /*
  854.   * Get the drawable for the current image...
  855.   */
  856.  
  857.   drawable = gimp_drawable_get (drawable_ID);
  858.   type     = gimp_drawable_type (drawable_ID);
  859.  
  860.  /*
  861.   * Set the image dimensions, bit depth, interlacing and compression
  862.   */
  863.  
  864.   png_set_compression_level (pp, pngvals.compression_level);
  865.  
  866.   info->width          = drawable->width;
  867.   info->height         = drawable->height;
  868.   info->bit_depth      = 8;
  869.   info->interlace_type = pngvals.interlaced;
  870.  
  871.  /* 
  872.   * Initialise remap[]
  873.   */
  874.   for (i = 0; i < 256; i ++)
  875.     {
  876.       remap[i] = i;
  877.     }
  878.  
  879.  /*
  880.   * Set color type and remember bytes per pixel count 
  881.   */
  882.  
  883.   switch (type)
  884.   {
  885.     case GIMP_RGB_IMAGE :
  886.         info->color_type = PNG_COLOR_TYPE_RGB;
  887.         bpp              = 3;
  888.         break;
  889.     case GIMP_RGBA_IMAGE :
  890.         info->color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  891.         bpp              = 4;
  892.         break;
  893.     case GIMP_GRAY_IMAGE :
  894.         info->color_type = PNG_COLOR_TYPE_GRAY;
  895.         bpp              = 1;
  896.         break;
  897.     case GIMP_GRAYA_IMAGE :
  898.         info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
  899.         bpp              = 2;
  900.         break;
  901.     case GIMP_INDEXED_IMAGE :
  902.         bpp              = 1;
  903.         info->color_type = PNG_COLOR_TYPE_PALETTE;
  904.         info->valid      |= PNG_INFO_PLTE;
  905.         info->palette= (png_colorp) gimp_image_get_cmap(image_ID, &num_colors);
  906.         info->num_palette= num_colors;
  907.         break;
  908.     case GIMP_INDEXEDA_IMAGE :
  909.         bpp              = 2;
  910.         info->color_type = PNG_COLOR_TYPE_PALETTE;
  911.         respin_cmap (pp, info, remap, image_ID, drawable); /* fix up transparency */
  912.         break;
  913.     default:
  914.         g_message ("%s\nImage type can't be saved as PNG", filename);
  915.         return 0;
  916.   };
  917.  
  918.  /*
  919.   * Fix bit depths for (possibly) smaller colormap images
  920.   */
  921.   
  922.   if (info->valid & PNG_INFO_PLTE) {
  923.     if (info->num_palette <= 2)
  924.       info->bit_depth= 1;
  925.     else if (info->num_palette <= 4)
  926.       info->bit_depth= 2;
  927.     else if (info->num_palette <= 16)
  928.       info->bit_depth= 4;
  929.     /* otherwise the default is fine */
  930.   }
  931.  
  932.   /* All this stuff is optional extras, if the user is aiming for smallest
  933.      possible file size she can turn them all off */
  934.  
  935. #if PNG_LIBPNG_VER > 99
  936.   if (pngvals.bkgd) {
  937.     gimp_palette_get_background(&red, &green, &blue);
  938.       
  939.     background.index = 0;
  940.     background.red = red;
  941.     background.green = green;
  942.     background.blue = blue;
  943.     background.gray = (red + green + blue) / 3;
  944.     png_set_bKGD(pp, info, &background);
  945.   }
  946.  
  947.   if (pngvals.gama) {
  948.     gamma = gimp_gamma();
  949.     png_set_gAMA(pp, info, 1.0 / (gamma != 1.00 ? gamma : DEFAULT_GAMMA));
  950.   }
  951.  
  952.   if (pngvals.offs) {
  953.     gimp_drawable_offsets(drawable_ID, &offx, &offy);
  954.     if (offx != 0 || offy != 0) {
  955.       png_set_oFFs(pp, info, offx, offy, PNG_OFFSET_PIXEL);
  956.     }
  957.   }
  958.  
  959.   if (pngvals.phys) {
  960.     gimp_image_get_resolution (orig_image_ID, &xres, &yres);
  961.     png_set_pHYs(pp, info, xres * 39.37, yres * 39.37, PNG_RESOLUTION_METER);
  962.   }
  963.  
  964.   if (pngvals.time) {
  965.     cutime= time(NULL); /* time right NOW */
  966.     gmt = gmtime(&cutime);
  967.  
  968.     mod_time.year = gmt->tm_year + 1900;
  969.     mod_time.month = gmt->tm_mon + 1;
  970.     mod_time.day = gmt->tm_mday;
  971.     mod_time.hour = gmt->tm_hour;
  972.     mod_time.minute = gmt->tm_min;
  973.     mod_time.second = gmt->tm_sec;
  974.     png_set_tIME(pp, info, &mod_time);
  975.   }
  976.  
  977. #endif /* PNG_LIBPNG_VER > 99 */
  978.  
  979.   png_write_info (pp, info);
  980.  
  981.  /*
  982.   * Turn on interlace handling...
  983.   */
  984.  
  985.   if (pngvals.interlaced)
  986.     num_passes = png_set_interlace_handling(pp);
  987.   else
  988.     num_passes = 1;
  989.  
  990.  /*
  991.   * Convert unpacked pixels to packed if necessary
  992.   */
  993.  
  994.   if (info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8)
  995.     png_set_packing(pp);
  996.  
  997.  /*
  998.   * Allocate memory for "tile_height" rows and save the image...
  999.   */
  1000.  
  1001.   tile_height = gimp_tile_height();
  1002.   pixel       = g_new(guchar, tile_height * drawable->width * bpp);
  1003.   pixels      = g_new(guchar *, tile_height);
  1004.  
  1005.   for (i = 0; i < tile_height; i ++)
  1006.     pixels[i]= pixel + drawable->width * bpp * i;
  1007.  
  1008.   gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, drawable->width,
  1009.                       drawable->height, FALSE, FALSE);
  1010.  
  1011.   for (pass = 0; pass < num_passes; pass ++)
  1012.   {
  1013.       /* This works if you are only writing one row at a time... */
  1014.     for (begin = 0, end = tile_height;
  1015.          begin < drawable->height;
  1016.          begin += tile_height, end += tile_height)
  1017.       {
  1018.         if (end > drawable->height)
  1019.           end = drawable->height;
  1020.  
  1021.         num = end - begin;
  1022.         
  1023.         gimp_pixel_rgn_get_rect (&pixel_rgn, pixel, 0, begin, drawable->width, num);
  1024.         if (info->valid & PNG_INFO_tRNS) 
  1025.           {
  1026.             for (i = 0; i < num; ++i) 
  1027.               {
  1028.                 fixed = pixels[i];
  1029.                 for (k = 0; k < drawable->width; ++k) 
  1030.                   {
  1031.                     fixed[k] = (fixed[k*2+1] > 127) ? 
  1032.                                remap[fixed[k*2]] : 
  1033.                                0;
  1034.                      
  1035.                   }
  1036.               }
  1037.             /* Forgot this case before, what if there are too many colors? */
  1038.           } 
  1039.         else if (info->valid & PNG_INFO_PLTE && bpp == 2) 
  1040.           {
  1041.             for (i = 0; i < num; ++i) 
  1042.               {
  1043.                 fixed = pixels[i];
  1044.                 for (k = 0; k < drawable->width; ++k) 
  1045.                   {
  1046.                     fixed[k] = fixed[k*2];
  1047.                   }
  1048.               }
  1049.           }
  1050.         
  1051.         png_write_rows (pp, pixels, num);
  1052.         
  1053.         gimp_progress_update (((double)pass + (double)end /
  1054.                     (double)info->height) / (double)num_passes);
  1055.       };
  1056.   };
  1057.  
  1058.   png_write_end (pp, info);
  1059. #if PNG_LIBPNG_VER < 10200      /* ?? Anyway, this function isn't in 1.2.0*/
  1060.   png_write_destroy (pp);
  1061. #endif
  1062.  
  1063.   g_free (pixel);
  1064.   g_free (pixels);
  1065.  
  1066.  /*
  1067.   * Done with the file...
  1068.   */
  1069.  
  1070.   free (pp);
  1071.   free (info);
  1072.  
  1073.   fclose (fp);
  1074.  
  1075.   return (1);
  1076. }
  1077.  
  1078. static void
  1079. save_ok_callback (GtkWidget *widget,
  1080.                   gpointer  data)
  1081. {
  1082.   runme = TRUE;
  1083.  
  1084.   gtk_widget_destroy (GTK_WIDGET (data));
  1085. }
  1086.  
  1087. static void respin_cmap (png_structp pp, 
  1088.                          png_infop info, 
  1089.                          guchar *remap,
  1090.                          gint32 image_ID,
  1091.                          GimpDrawable *drawable) 
  1092. {
  1093.   static const guchar trans[] = { 0 };
  1094.   gint colors;
  1095.   guchar *before;
  1096.   gint transparent;
  1097.   gint cols, rows;
  1098.   GimpPixelRgn pixel_rgn;
  1099.   guchar *pixels;
  1100.  
  1101.   before= gimp_image_get_cmap(image_ID, &colors);
  1102.   cols = drawable->width;
  1103.   rows = drawable->height;
  1104.  
  1105.   gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
  1106.                        drawable->width, drawable->height, 
  1107.                        FALSE, FALSE);
  1108.  
  1109.   pixels = (guchar *) g_malloc (drawable->width *
  1110.                                 drawable->height * 2);
  1111.   
  1112.   gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, 0,
  1113.                            drawable->width, drawable->height);
  1114.  
  1115.  
  1116.   /* Try to find an entry which isn't actually used in the
  1117.      image, for a transparency index. */
  1118.   
  1119.   transparent = find_unused_ia_colour(pixels,
  1120.                                       drawable->width * drawable->height,
  1121.                                       &colors);
  1122.   
  1123. #if PNG_LIBPNG_VER > 99
  1124.   if (transparent != -1)  /* we have a winner for a transparent 
  1125.                            * index - do like gif2png and swap 
  1126.                            * index 0 and index transparent */
  1127.     {
  1128.       png_color palette[256];
  1129.       gint i;
  1130.       
  1131.       png_set_tRNS(pp, info, (png_bytep) trans, 1, NULL);
  1132.  
  1133.       /* Transform all pixels with a value = transparent to 
  1134.        * 0 and vice versa to compensate for re-ordering in palette 
  1135.        * due to png_set_tRNS() */
  1136.  
  1137.       remap[0] = transparent;
  1138.       remap[transparent] = 0;
  1139.       
  1140.       /* Copy from index 0 to index transparent - 1 to index 1 to 
  1141.        * transparent of after, then from transparent+1 to colors-1 
  1142.        * unchanged, and finally from index transparent to index 0. */
  1143.  
  1144.       for(i = 0; i < colors; i++)
  1145.         {
  1146.           palette[i].red = before[3 * remap[i]];
  1147.           palette[i].green = before[3 * remap[i] + 1];
  1148.           palette[i].blue = before[3 * remap[i] + 2];
  1149.         }
  1150.  
  1151.       /* Set index on all transparent pixels to 0 */
  1152.       
  1153.       png_set_PLTE(pp, info, palette, colors);
  1154.     } 
  1155.   else 
  1156.     {
  1157.       /* Inform the user that we couldn't losslessly save the 
  1158.        * transparency & just use the full palette */
  1159.       g_message ( _("Couldn't losslessly save transparency, saving opacity instead.\n"));
  1160.       png_set_PLTE (pp, info, (png_colorp) before, colors);
  1161.     }
  1162. #else
  1163.   info->valid     |= PNG_INFO_PLTE;
  1164.   info->palette=     (png_colorp) before;
  1165.   info->num_palette= colors;
  1166. #endif /* PNG_LIBPNG_VER > 99 */
  1167.  
  1168.   g_free (pixels);
  1169.  
  1170. }
  1171.  
  1172. static gint
  1173. save_dialog (void)
  1174. {
  1175.   GtkWidget *dlg;
  1176.   GtkWidget *frame;
  1177.   GtkWidget *table;
  1178.   GtkWidget *toggle;
  1179.   GtkWidget *scale;
  1180.   GtkObject *scale_data;
  1181.  
  1182.   dlg = gimp_dialog_new (_("Save as PNG"), "png",
  1183.                          gimp_standard_help_func, "filters/png.html",
  1184.                          GTK_WIN_POS_MOUSE,
  1185.                          FALSE, TRUE, FALSE,
  1186.  
  1187.                          _("OK"), save_ok_callback,
  1188.                          NULL, NULL, NULL, TRUE, FALSE,
  1189.                          _("Cancel"), gtk_widget_destroy,
  1190.                          NULL, 1, NULL, FALSE, TRUE,
  1191.  
  1192.                          NULL);
  1193.  
  1194.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  1195.                       GTK_SIGNAL_FUNC (gtk_main_quit),
  1196.                       NULL);
  1197.  
  1198.   frame = gtk_frame_new (_("Parameter Settings"));
  1199.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  1200.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  1201.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
  1202.   gtk_widget_show (frame);
  1203.  
  1204.   table = gtk_table_new (2, 7, FALSE);
  1205.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  1206.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  1207.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  1208.   gtk_container_add (GTK_CONTAINER (frame), table);
  1209.   gtk_widget_show (table);
  1210.  
  1211.   toggle = gtk_check_button_new_with_label (_("Interlacing (Adam7)"));
  1212.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 0, 1,
  1213.                     GTK_FILL, 0, 0, 0);
  1214.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1215.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1216.                       &pngvals.interlaced);
  1217.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.interlaced);
  1218.   gtk_widget_show (toggle);
  1219.  
  1220.   toggle = gtk_check_button_new_with_label (_("Save background color"));
  1221.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 1, 2,
  1222.                     GTK_FILL, 0, 0, 0);
  1223.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1224.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1225.                       &pngvals.bkgd);
  1226.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.bkgd);
  1227.   gtk_widget_show (toggle);
  1228.  
  1229.   toggle = gtk_check_button_new_with_label (_("Save gamma"));
  1230.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 2, 3,
  1231.                     GTK_FILL, 0, 0, 0);
  1232.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1233.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1234.                       &pngvals.gama);
  1235.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.gama);
  1236.   gtk_widget_show (toggle);
  1237.  
  1238.   toggle = gtk_check_button_new_with_label (_("Save layer offset"));
  1239.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 3, 4,
  1240.                     GTK_FILL, 0, 0, 0);
  1241.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1242.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1243.                       &pngvals.offs);
  1244.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.offs);
  1245.   gtk_widget_show (toggle);
  1246.  
  1247.   toggle = gtk_check_button_new_with_label (_("Save resolution"));
  1248.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 4, 5,
  1249.                     GTK_FILL, 0, 0, 0);
  1250.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1251.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1252.                       &pngvals.phys);
  1253.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.phys);
  1254.   gtk_widget_show (toggle);
  1255.  
  1256.   toggle = gtk_check_button_new_with_label (_("Save creation time"));
  1257.   gtk_table_attach (GTK_TABLE (table), toggle, 0, 2, 5, 6,
  1258.                     GTK_FILL, 0, 0, 0);
  1259.   gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
  1260.                       GTK_SIGNAL_FUNC (gimp_toggle_button_update),
  1261.                       &pngvals.time);
  1262.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), pngvals.time);
  1263.   gtk_widget_show (toggle);
  1264.  
  1265.   scale_data = gtk_adjustment_new (pngvals.compression_level,
  1266.                                    0.0, 9.0, 1.0, 1.0, 0.0);
  1267.   scale      = gtk_hscale_new (GTK_ADJUSTMENT (scale_data));
  1268.   gtk_widget_set_usize (scale, SCALE_WIDTH, 0);
  1269.   gtk_scale_set_value_pos (GTK_SCALE (scale), GTK_POS_TOP);
  1270.   gtk_scale_set_digits (GTK_SCALE (scale), 0);
  1271.   gtk_range_set_update_policy (GTK_RANGE (scale), GTK_UPDATE_DELAYED);
  1272.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 6,
  1273.                              _("Compression Level:"), 1.0, 1.0,
  1274.                              scale, 1, FALSE);
  1275.   gtk_signal_connect (GTK_OBJECT (scale_data), "value_changed",
  1276.                       GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  1277.                       &pngvals.compression_level);
  1278.   gtk_widget_show (scale);
  1279.  
  1280.   gtk_widget_show (dlg);
  1281.  
  1282.   gtk_main ();
  1283.   gdk_flush ();
  1284.  
  1285.   return runme;
  1286. }
  1287.